﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using NobleTech.Products.CreatureKingdom.Utils;
using NobleTech.Products.CreatureKingdom.WPFClient.Controls;

namespace NobleTech.Products.CreatureKingdom.WPFClient
{
    
    /// <summary>
    /// Interaction logic for World.xaml
    /// </summary>
    public partial class WorldWindow : Window
    {
        World world;
        MapOrganisms MapOrganisms = new MapOrganisms();

        
        bool IsRunning = false;
        System.Threading.Timer timer;
        
        public WorldWindow()
        {
            InitializeComponent();
            lOrganisms.ItemsSource = LoadedOrganisms;
            cMap.ItemsSource = MapOrganisms;
            Restart();

            System.Threading.TimerCallback timercallback = new System.Threading.TimerCallback(TimedEvent);
            timer = new System.Threading.Timer(timercallback, null, 100, 100);            
        }

        private void TimedEvent(object StateObject)
        {
            if(IsRunning)
                world.DoMove();
            cMap.Dispatcher.Invoke((Action)delegate { RefreshScreen(); });
        }

        private void Restart()
        {
            world = new World();
            sZoom.Value = 4;
            cMap.MapPositionX = 5000;
            cMap.MapPositionY = 5000;
            LoadedOrganisms.Clear();            
            CreateOrganisms();            
        }

        private void CreateOrganisms()
        {
            System.IO.FileInfo sampleOrgsDllInfo = new System.IO.FileInfo("../../../Sample Organisms/bin/Debug/Sample Organisms.dll");
            string ErrorLog = LoadAssembly(sampleOrgsDllInfo.FullName);
                if(ErrorLog!="")
                    MessageBox.Show(ErrorLog);
            foreach(OrganismInformation OrganismInformation in LoadedOrganisms)
            {
                world.AddOrganism(OrganismInformation.ImplementationType);
                world.AddOrganism(OrganismInformation.ImplementationType);
                world.AddOrganism(OrganismInformation.ImplementationType);
            }
        }

        System.Collections.ObjectModel.ObservableCollection<OrganismInformation> LoadedOrganisms = new System.Collections.ObjectModel.ObservableCollection<OrganismInformation>();
        
    

        private void bLoad_Click(object sender, RoutedEventArgs e)
        {
            
            // Configure open file dialog box
            Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog();
            dlg.FileName = ""; // Default file name
            dlg.DefaultExt = ".dll"; // Default file extension
            dlg.Filter = "DLL (.dll)|*.dll"; // Filter files by extension

            // Show open file dialog box
            Nullable<bool> result = dlg.ShowDialog();

            // Process open file dialog box results
            if (result == true)
            {
                string ErrorLog = LoadAssembly(dlg.FileName);
                if(ErrorLog!="")
                    MessageBox.Show(ErrorLog);
            }
            
        }

        public string LoadAssembly(string Filename)
        {
            string ErrorLog ="";
            foreach (OrganismInformation OI in LoadedOrganisms)
                if (OI.FileName.ToUpper() == Filename.ToUpper())
                {
                    return "This assembly has already been loaded.";
                }

            OrganismLoader.LoadResults ldRes = OrganismLoader.FromPath(Filename);
            if (ldRes.AssemblyErrors.HasErrors)
                ErrorLog += "Assembly Error: " + ldRes.AssemblyErrors.Message + " " + ldRes.AssemblyName + " " + ldRes.AssemblyVersion.ToString() + " " + Filename + "\r\n";
            else
                foreach (OrganismLoader.LoadResults.OrganismType orgType in ldRes.Organisms)
                {
                    if (orgType.OrganismErrors.HasErrors)
                        ErrorLog += "Organism Error: " + orgType.ImplementationType.Name + " " + orgType.OrganismErrors.Message + "\r\n";
                    else
                    {
                        OrganismInformation OrgInfo = new OrganismInformation();
                        OrgInfo.Name = orgType.ImplementationType.Name;
                        OrgInfo.AssemblyName = ldRes.AssemblyName;
                        OrgInfo.AssemblyVersion = ldRes.AssemblyVersion.ToString();
                        OrgInfo.FileName = Filename;
                        OrgInfo.ImplementationType = orgType.ImplementationType;
                        
                        LoadedOrganisms.Add(OrgInfo);
                        
                    }
                }                        
            return ErrorLog;
        }
     
        
        private void RefreshScreen()
        {
            List<string> Stats = new List<string>();

            double ViewPortHeight = (cMap.ActualHeight) / cMap.MapZoom;
            double ViewPortWidth = (cMap.ActualWidth) / cMap.MapZoom;
            double ViewPortTop = cMap.MapPositionY - ViewPortHeight/2;
            double ViewPortLeft = cMap.MapPositionX - ViewPortWidth/2;
            
            ViewPort viewPort = world.GetViewPort(new RectangleD(new PointD(ViewPortLeft,ViewPortTop), new SizeD(ViewPortWidth,ViewPortHeight)));

            IEnumerable<Organism> IEO = viewPort.GetOrganisms();
            foreach (Organism org in IEO)
            {
                MapOrganism MO;
                if (!MapOrganisms.Select(o => o.ID).Contains(org.ID))
                {
                    MO = new MapOrganism();
                    MO.ID = org.ID;
                    MapOrganisms.Add(MO);
                }
                else
                    MO = MapOrganisms.Where(o => o.ID==org.ID).First();

                MO.Name = org.TypeName;
                MO.PositionX = org.Location.X;
                MO.PositionY = org.Location.Y;
                MO.Size = org.Size;
                MO.DirectionFacing = org.directionFacing;
                MO.IsAlive = org.IsAlive;
                MO.Texture = org.renderer.Render(null, new PointD(0, 0), 0d);                
            }
            
            var CurrentIDs = IEO.Select(p=>p.ID).ToArray();
            var ToDelete = MapOrganisms.Where(o=>!CurrentIDs.Contains(o.ID)).ToArray();
            foreach (MapOrganism MO in ToDelete)
                MapOrganisms.Remove(MO);

            if (cMap.SelectedItem != null)
            {
                MapOrganism MO = cMap.SelectedItem as MapOrganism;
                if (MO != null)
                {
                    Stats.Clear();
                    Stats.Add(string.Format("Name: {0}", MO.Name));
                    Stats.Add(string.Format("Position (X:Y): {0:0.00}:{1:0.00}", MO.PositionX, MO.PositionY));
                    Stats.Add(string.Format("Size: {0}", MO.Size));
                    Stats.Add(string.Format("Direction facing: {0}", MO.DirectionFacing*180/Math.PI));
                }
            }
    
            if(Stats.Count==0)
                Stats.Add("Select an organism to display stats");
            cStats.ItemsSource = Stats;
            
        }

        private void bGo_Click(object sender, RoutedEventArgs e)
        {
            IsRunning = !IsRunning;
            bGo.Content = IsRunning ? "Stop" : "Go";
                
        }

        private void bRestart_Click(object sender, RoutedEventArgs e)
        {
            Restart();
        }

        private void bAdd_Click(object sender, RoutedEventArgs e)
        {
            if (lOrganisms.SelectedIndex != -1)
                world.AddOrganism(LoadedOrganisms[lOrganisms.SelectedIndex].ImplementationType);
        }

      }
}
